<?php

class UvmFrontController extends UvmGeneric
{
    
    const REQUEST_URI = 'REQUEST_URI';
    const REQUEST_ROOT = 'api.be';
    
    const PAGESIZE_DEFAULT = '80';
    
    private $_lib_root = null;
    private $_raw_path = null;
    private $_post = array();
    private $_get  = array();
    private $_request = array();
    private $_mapper = array(
        1 => 'root',
        2 => 'entity'
    );
    private $_valid = array(
        'root' => array('api', 'api.be'),
        'entity' => array('UvmPredefRelations.js', 'persist.json', 'relations.json', 'assembly.json', 'assemblies.json', 'item.json', 'items.json', 'module.json', 'modules.json', 'assembly_item.json', 'assembly_items.json', 'program.json', 'programs.json')
    );
    
    private $_tables = array(
        'assembly' => array('assembly.json', 'assemblies.json'),
        'item' => array('item.json', 'items.json'),
        'module' => array('modules.json', 'module.json'),
        'program' => array('program.json', 'programs.json'),
        'assembly_item' => array('assembly_item.json', 'assembly_items.json'),
    );
    
    private $_getters = array(
        'assembly.json', 'assembly_item.json', 'item.json', 'program.json', 'module.json', 'relations.json'
    );
    
    private $_write = array(
        'persist.json'
    );
    
    private $_searchable = array(
        'program' => array(
            'like'         => true
        ),
        'assembly' => array(
            'fk_module_id' => true,
            'like'         => true
        ),
        'module' => array(
            'fk_program_id' => true,
            'like'          => true
        ),
        'item' => array(
            'fk_module_id' => true,
            'like'         => true
        ),
        'assembly_item' => array(
            'fk_module_id'   => true,
            'fk_item_id'     => true,
            'fk_assembly_id' => true,
            'like'           => true
        ),        
    );
    
    
    public static function sanitizeString($string, $extend=null)
    {
        $chars   = '._-';        // 'harmless' characters
        
        if (is_string($extend)) {
            
            $_chars = self::extractUniqueCharacters(
                $chars, 
                false, 
                self::RETURN_AS_ARRAY
            );
            
            $_extnd = self::extractUniqueCharacters(
                $extend, 
                false, 
                self::RETURN_AS_ARRAY
            );
            
            $extend = array();
            
            foreach ($_extnd as $_char) {
                if (!in_array($_char, $_chars)) {
                    $extend[] = $_char;
                }
            }
            if (count($extend)>0) {
                $extend = join('', $extend);
                $extend = "([^".(string) preg_quote($extend)."])";
            } else {
                $extend = '';
            }
            
        } else {
            $extend = '';
        }
        
        $replace = "/([^a-zA-Z0-9.,:".$chars."\s]){$extend}/";
        
        return (string) preg_replace($replace, "", $string);        
    }
    
    public static function sanitizeArray($input)
    {
        if (!is_array($input) || empty($input)) {
            return array();
        } else {
            $tmp = array();
            foreach ($input as $key => $value) {
                $tmp[self::sanitizeString($key)] = self::sanitizeString($value);
            }
            return $tmp;
        }
    }
    
    
    public function __construct($server, $get=array(), $post=array(), $libroot)
    {
        $this->_lib_root = $libroot;
        $this->_get  = self::sanitizeArray($get);
        $this->_post = self::sanitizeArray($post);
        $this->_raw_path = (isset($server[self::REQUEST_URI])) ? $server[self::REQUEST_URI] : null;
        $this->processPath($this->_raw_path);
    }
    
    public function isValidRequest()
    {
        return !empty($this->_request);
    }
    
    public function render()
    {
        
        /**
         * Regularr entry points -----------------
         */
        //trap($this->_lib_root.$this->_raw_path);
        $direct = realpath($this->_lib_root.str_replace('/api.be/', '/', $this->_raw_path));
        
        if (!is_bool($direct)) {
            echo file_get_contents($direct);
            exit();
        }
        
        /**
         * Mock for relations JSON
         */
        if (isset($this->_request['entity']) && 'relations.json'==$this->_request['entity']) {
            $jsonstring = file_get_contents(dirname(__FILE__).'/relations.json.mock');
            $data       = json_decode($jsonstring, true);
            return $data;
        }
        
        
        /**
         * Dynamic JS class
         */
        if (isset($this->_request['entity']) && 'UvmPredefRelations.js'==$this->_request['entity']) {
            $rels = new UvmPredefRelations();
            echo $rels->render();
            exit();
        }
        
        
        /**
         * Regularr entry points -----------------
         */
        
        $table  = null;
        $entity = isset($this->_request['entity']) ? $this->_request['entity'] : null;
        
        foreach ($this->_tables as $tab => $aliases) {
            $aliases = array_flip($aliases);
            if (isset($aliases[$entity])) {
                $table = $tab;
            }
        }
        $action = (in_array($entity, $this->_getters)) ? 'get' : 'feed';
        if (in_array($entity, $this->_write)) {
            $action = 'write';
        }
        
        switch ($action) {
            case 'get':
                $model = new UvmModel($table);
                return $model->get((!isset($this->_get['id'])) ? null : $this->_get['id']);
                break;
            
            case 'write':
                $updates = $this->listUpdates();
                $model = new UvmModel('mapping');
                $response = array();
                
                if (isset($updates['persist'])) {
                    foreach ($updates['persist'] as $xpath => $value) {
                        $model->replaceIntoMappings($xpath, $value);
                        $response[$xpath] = true;
                    }
                    //echo (!empty($response) && !in_array(false, $response)) ? 'ACK' : 'ERROR';
                    echo (!empty($response) && !in_array(false, $response)) ? 'ACK' : 'ERROR';
                    exit();
                    
                } else {
                    echo 'ERROR-EMPTY-LIST';
                    exit();
                }
                return $response;
                break;
            
            case 'feed':
                $model = new UvmModel($table);
                $ids    = (!isset($this->_get['ids'])) ? null : $this->_get['ids'];
                $filter = null;
                if (isset($this->_searchable[$table])) {
                    $filter = array();
                    foreach ($this->_get as $key => $value) {
                        if (isset($this->_searchable[$table][$key]) && true===$this->_searchable[$table][$key]) {
                            $filter[$key] = $value;
                        }
                    }
                    if (empty($filter)) {
                        $filter = null;
                    }
                    
                    if (isset($filter['like'])) {
                        $filter = array(
                            'like' => $filter['like']
                        ); // LIKE overwrites all filters
                    }
                }
                
                
                return $model->feed($ids, $filter);
                break;
            
            case 'find':
                throw new InvalidArgumentException('Search not implemented');
                break;
            
            default:
                throw new InvalidArgumentException('Action not mapped');
                break;
            
        }
        
    }
    
    public function listUpdates()
    {
        $data = array();
        if (isset($_POST['data'])) {
            $data = (array) @json_decode(stripslashes($_POST['data']), true);
            if (isset($_POST['queue_id'])) {
                $data['queue_id'] = $_POST['queue_id'];
            }
            if (isset($data['persist'])) {
                return $data;
            }
        }
        return array();
    }
    
    
    public function dispatch($xc=null)
    {
        $envelope = $this->_envelope();
        try {
            
            if (is_object($xc) && ($xc instanceof Exception)) {
                throw $xc;
            }
            
            $envelope['result'] = $this->render();
            
        } catch (Exception $x) {
            $envelope['error']   = true;
            $envelope['message'] = $x->getMessage();
        }
        
        header("Content-Type: application/json");
        echo json_encode($envelope);
        exit();
        
    }
        
    
    private function _envelope()
    {
        return array(
            'error' => false,
            'message' => false,
            'meta' => array(
                'request'   => $this->_request,
                'generated' => @date('r'),
                'cached'    => false,
                'pagesize'  => self::PAGESIZE_DEFAULT // @todo: to be refactored
            ),
            'result' => array()
        );
    }
    
    
    
    public function validatePath($array)
    {
        if (!is_array($array) || empty($array) || in_array(false, $array)) {
            return array();
        } else {
            $check = array();
            foreach ($array as $key => $value) {
                if (!isset($this->_valid[$key]) || !in_array($value, $this->_valid[$key])) {
                    $check[$key] = false;
                } else {
                    $check[$key] = true;
                }
            }
            return (in_array(false, $check)) ? array() : $array;
        }
    }
    
    public function processPath($path)
    {
        $path = str_replace(array('&', '?'), '/', $path);
        $expl = explode('/', $path);
        
        $tmp = array();
        foreach ($this->_mapper as $pos => $alias) {
            $tmp[$alias] = (isset($expl[$pos])) ? trim($expl[$pos]) : false;
        }
        $this->_request = $this->validatePath($tmp);
    }
    
    
    
    
    
    
    
    
    
}
